from classfile.class_reader import MethodCodeReader
from insts.inst import Instruction
from jthread.frame import JFrame


class LOOKUP_SWITCH(Instruction):

    def __init__(self, opcode):
        super().__init__(opcode)
        self.default_offset = None
        self.npairs = None
        self.match_offsets = []

    def fetchOperands(self, reader: MethodCodeReader):
        reader.skipPadding()
        self.default_offset = reader.readUInt32()
        self.npairs = reader.readUInt32()
        self.match_offsets = reader.readBytes(self.npairs * 2)

    def branch(self, frame: JFrame, offset):
        pc = frame.getJThread().getPC()
        nextPc = pc + offset
        frame.setNextPc(nextPc)

    def execute(self, frame):
        key = frame.getOperandStack().popInt()
        for i in range(0, self.npairs * 2, 2):
            match = self.match_offsets[i]
            offset = self.match_offsets[i + 1]
            if key == match:
                self.branch(frame, offset)
                return
        self.branch(frame, self.default_offset)


class TABLE_SWITCH(Instruction):

    def __init__(self, opcode):
        super().__init__(opcode)
        self.default_offset = None
        self.low = None
        self.high = None
        self.jump_offsets = []

    def fetchOperands(self, reader: MethodCodeReader):
        reader.skipPadding()
        self.default_offset = reader.readUInt32()
        self.low = reader.readUInt32()
        self.high = reader.readUInt32()
        self.jump_offsets = reader.readBytes(self.high - self.low + 1)

    def branch(self, frame: JFrame, offset):
        pc = frame.getJThread().getPC()
        nextPc = pc + offset
        frame.setNextPc(nextPc)

    def execute(self, frame):
        index = frame.getOperandStack().popInt()
        offset = 0
        if index >= self.low and index <= self.high:
            offset = self.jump_offsets[index - self.low]
            self.branch(frame, offset)
        else:
            self.branch(frame, self.default_offset)
